package org.ddd.orderdemo.application.event.publish;

import com.google.common.eventbus.EventBus;
import org.ddd.orderdemo.application.event.Event;
import org.ddd.orderdemo.application.event.SlowEvent;
import org.ddd.orderdemo.application.event.center.EventBusExceptionHandler;
import org.ddd.orderdemo.application.event.center.EventBusFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 参考nacos通知机制
 * com.alibaba.nacos.common.notify.NotifyCenter
 */
public class NotifyCenter {

    private static final Logger logger = LoggerFactory.getLogger(EventBusExceptionHandler.class);

    private static final NotifyCenter INSTANCE = new NotifyCenter();

    private DefaultPublisher sharePublisher;

    /**
     * Publisher management container.
     */
    private final Map<String, EventPublisher> publisherMap = new ConcurrentHashMap<>(16);

    static {
        try {

            EventBus eventBus = EventBusFactory.createEventBus();
            // Create and init DefaultPublisher instance.
            INSTANCE.sharePublisher = new DefaultPublisher(eventBus);

        } catch (Throwable ex) {
            logger.error("Service class newInstance has error : ", ex);
        }

    }

    /**
     * Request publisher publish event Publishers load lazily, calling publisher. Start () only when the event is
     * actually published.
     *
     * @param event class Instances of the event.
     */
    public static boolean publishEvent(final Event event) {
        try {
            return publishEvent(event.getClass(), event);
        } catch (Throwable ex) {
            logger.error("There was an exception to the message publishing : ", ex);
            return false;
        }
    }

    /**
     * Request publisher publish event Publishers load lazily, calling publisher.
     *
     * @param eventType class Instances type of the event type.
     * @param event     event instance.
     */
    private static boolean publishEvent(final Class<? extends Event> eventType, final Event event) {


        final String topic = getCanonicalName(eventType);

        EventPublisher publisher = INSTANCE.publisherMap.get(topic);
        if (publisher != null) {
            return publisher.publish(event);
        }

        logger.warn("There are no [{}] publishers for this event, please register", topic);

        return false;
    }


    /**
     * Register publisher.
     *
     * @param eventType class Instances type of the event type.
     * @param publisher the specified event publisher
     */
    public static void registerToPublisher(final Class<? extends Event> eventType, final EventPublisher publisher) {
        if (null == publisher) {
            return;
        }

        if (isAssignableFrom(SlowEvent.class, eventType)) {
//             INSTANCE.sharePublisher;
        }

        final String topic =  getCanonicalName(eventType);
        synchronized (NotifyCenter.class) {
            INSTANCE.publisherMap.putIfAbsent(topic, publisher);
        }
    }

    public static boolean isAssignableFrom(Class clazz, Class cls) {
        Objects.requireNonNull(cls, "cls");
        return clazz.isAssignableFrom(cls);
    }

    /**
     * Gets and returns the canonical name of the underlying class.
     *
     * @param cls Instances of the class represent classes and interfaces.
     * @return The canonical name of the underlying class.
     */
    public static String getCanonicalName(Class cls) {
        Objects.requireNonNull(cls, "cls");
        return cls.getCanonicalName();
    }

    /**
     * Deregister publisher.
     *
     * @param eventType class Instances type of the event type.
     */
    public static void deregisterPublisher(final Class<? extends Event> eventType) {
        final String topic = getCanonicalName(eventType);
        EventPublisher publisher = INSTANCE.publisherMap.remove(topic);
        try {
//            publisher.shutdown();
        } catch (Throwable ex) {
            logger.error("There was an exception when publisher shutdown : ", ex);
        }
    }

}
